#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <fstream>
#include "json.hpp"

typedef unsigned char       uint8_t;
typedef unsigned char       uint8_t;
typedef unsigned short int      uint16_t;
typedef unsigned int        uint32_t;
#define SDC_METHOD_CREATE 1
#define SDC_METHOD_GET 2
#define SDC_METHOD_UPDATE 3
#define SDC_METHOD_DELETE 4
//网关服务
#define SDC_GATEWAY_SERVER   "/mnt/srvfs/gateway.paas.sdc"
//网关配置文件,portal.conf配置文件必须被打包到APP包根目录下
#define APP_PORTAL_CONF_PATH "./portal.conf"
// #define SDC_VERSION 0x01
#define SDC_URL_DEMO	0x00
#define MAXFD 100
#include "sdc.h"
#include "hw_app_log.h"
#include "AppEpoll.h"
#include "httpParser.hpp"
#include "types.h"

extern GC_APP_CONFIG gc_app_config;

using namespace std;
using json = nlohmann::json;

std::string json_mess_header = "HTTP/1.1 200 OK\r\nContent-Type: application/json;charset=UTF-8\r\n\r\n";
std::string message = "";

typedef struct{
	uint16_t version;
	uint8_t	url_ver;
	uint8_t method: 7;
	uint8_t response: 1;
	uint16_t url;
	uint16_t code;
	uint16_t head_length;
	uint16_t trans_id;
	uint32_t content_length;
}sdc_common_head;

typedef struct
{
    char register_path[256];
}PASS_GATEWAY_REGISTER;

int create_socket()//创建服务端域套接字/tmp/epollSocket
{
    unlink("/tmp/epollSocket");	//解除原有epollSocket对象链接
    int listenfd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un server_address;
    //配置server_address
    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "/tmp/epollSocket");
    int server_len = sizeof(server_address);
    //创建域套接字
    bind(listenfd, (struct sockaddr *)&server_address, server_len);
    //监听请求
    listen(listenfd, 5);
    return listenfd;
}
 
void get_client_link(int listenfd, int epfd)//获取一个客户端连接
{
    struct sockaddr_un client_address;
    int client_len = sizeof(client_address);
    //accept函数接收客户端请求，存储客户端地址信息、客户端地址大小
    int client_sockfd = accept(listenfd,(struct sockaddr *)&client_address, (socklen_t *)&client_len);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = client_sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, client_sockfd, &ev);
    LOG_DEBUG("get_client_link sockfd is %d",client_sockfd);
}
 
void unlink_client(int fd, int epfd)//断开连接
{
    close(fd);
    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
    LOG_DEBUG("one client unlink\n");
}
 
void deal_client_data(int fd, int epfd,struct epoll_event ev)//处理客户端数据
{
    char buff[10240];
    int res = read(fd, buff, sizeof(buff));
    // printf("**********client data is: %s\n",buff);
    HttpRequest req;
	req.tryDecode(buff);

    // TODO 请求数据过滤
    json j_res;
    if (req.getUrl()=="/SDCAPI/V1.0/AIPlus/algv")
    {
        if (req.getMethod()=="POST")
        {
            std::string body_s = req.getBody();
            json configData = json::parse(body_s);
            std::ofstream ofs("./config/app_config.json",std::fstream::out);
            ofs << configData;
            ofs.close();
            j_res["code"] = "0";
            j_res["desc"] = "参数更新成功";
            message = json_mess_header + j_res.dump();

            if (configData.contains("test")) {
                if (configData.at("test").at("enable") == 1) {
                    gc_app_config.algv_host = configData.at("test").at("ip").get<std::string>() + ":" + to_string(configData.at("test").at("port").get<int>());
                    LOG_INFO("set algv host to test env: %s, wait to restart", gc_app_config.algv_host.c_str());
                }
            }
            if (configData.contains("pro")) {
                if (configData.at("pro").at("enable") == 1) {
                    gc_app_config.algv_host = configData.at("pro").at("ip").get<std::string>() + ":" + to_string(configData.at("pro").at("port").get<int>());
                    LOG_INFO("set algv host to pro env: %s, wait to restart", gc_app_config.algv_host.c_str());
                }
            }
        }
        else if (req.getMethod()=="GET")
        {
            json registerData;
            std::ifstream ifs("./config/app_config.json",std::fstream::in);
            ifs >> registerData;
            ifs.close();
            j_res["code"] = "0";
            j_res["desc"] = "默认参数加载成功";
            j_res["params"] = registerData;
            message = json_mess_header + j_res.dump();
        }
        else
        {
            j_res["code"] = 1;
            j_res["desc"] = "无此方法";
            message = json_mess_header + j_res.dump();
        }
    } 
    else
    {
        j_res["code"] = 1;
        j_res["desc"] = "无此url";
        message = json_mess_header + j_res.dump();
    }

    fflush(stdout);
    fsync(fd);
    ev.data.fd=fd;
    ev.events = EPOLLOUT | EPOLLET;
    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
}

void send_service_message(int fd, int epfd)//发送数据
{
    int num = write(fd, message.c_str(), message.size());
    // LOG_DEBUG("write return length is : %d/%d",num,message.size());
    unlink_client(fd, epfd);
    message = "";
}

void deal_finish_fd(int n, struct epoll_event *events, int epfd, int listenfd,struct epoll_event ev)//处理就绪的文件描述符
{
    int i = 0;
    for(i = 0;i <= n;i++)
    {
        int fd = events[i].data.fd;
        // LOG_DEBUG("deal_finish_fd fd is %d",fd);
        if(fd == listenfd)
        {
            get_client_link(listenfd, epfd);
        }
        else if(events[i].events & EPOLLHUP)
		{
			unlink_client(fd, epfd);
		}
        else if(events[i].events & EPOLLIN)
        {
            deal_client_data(fd, epfd, ev);
        }
        else if(events[i].events & EPOLLOUT)
        {
            send_service_message(fd, epfd);
        }
        else
        {
            LOG_ERROR("fd error");
        }
    }   
}

void startServer()
{
    int listenfd = create_socket();
    LOG_INFO("create_socket listenfd is %d",listenfd);
    int epfd = epoll_create(128);
    LOG_INFO("epoll_create epfd is %d",epfd);
    struct epoll_event ev;
    ev.data.fd = listenfd;
    ev.events = EPOLLIN;
    int result = epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);//在内核事件中注册listenfd
    LOG_INFO("epoll_ctl is %d",result);
    struct epoll_event events[MAXFD];
    while(1)
    {
        int n = epoll_wait(epfd, events, sizeof(events)/sizeof(events[0]), -1);
        LOG_INFO("epoll_wait is %d",n);
        deal_finish_fd(n, events, epfd, listenfd,ev);
    }
}

int registerGateway()
{
    PASS_GATEWAY_REGISTER gatewayrst;
    int gateway = snprintf(gatewayrst.register_path,sizeof(gatewayrst.register_path),APP_PORTAL_CONF_PATH);
    LOG_INFO("register_path gateway is %d",gateway);
    LOG_INFO("register_path is %s",gatewayrst.register_path);
    sdc_common_head head;
    head.version = SDC_VERSION; //0x5331
    head.url = SDC_URL_DEMO; //0x00
    head.method = SDC_METHOD_CREATE;
    head.head_length = sizeof(head);
    head.content_length = sizeof(gatewayrst);
 
    int fd = open(SDC_GATEWAY_SERVER, O_RDWR);
    LOG_INFO("SDC_GATEWAY_SERVER fd is %d, %s",fd,strerror(errno));
    if(fd < 0) {
        return 0;
    }
    struct iovec iov[] = {
        {.iov_base = &head, .iov_len = sizeof(head) },
        {.iov_base = &gatewayrst, .iov_len = sizeof(gatewayrst) }
    };
    int nret = writev(fd, iov, sizeof(iov)/sizeof(iov[0]));
    LOG_INFO("SDC_GATEWAY_SERVER write nret is %d, %s",nret,strerror(errno));
    if(nret < 0) {
        return 0;
    }
    nret = readv(fd,iov,2);
    LOG_INFO("SDC_GATEWAY_SERVER read nret is %d, %s",nret,strerror(errno));
    LOG_INFO("head code num is %d",head.code);
    return 1;
}